package org.springframework.validation.webflow;

import java.lang.reflect.ParameterizedType;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.validation.WebflowValidationManager;
import org.springframework.webflow.execution.RequestContextHolder;

public class BaseWebflowValidator<T> implements Validator {
	
	@Autowired
	private WebflowValidationManager validationManager;
	
	protected Class<T> entityClass;
	
	public BaseWebflowValidator() {
		this.entityClass = calculateEntityClass();
	}
	
	@SuppressWarnings("unchecked")
	protected Class<T> calculateEntityClass() {
		Object clazz = ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
		if (ParameterizedType.class.isAssignableFrom(clazz.getClass())) {
			return (Class<T>) ((ParameterizedType) clazz).getRawType();
		} else {
			return (Class<T>) clazz;
		}
	}

	@Override
	public boolean supports(Class<?> clazz) {
		return entityClass.isAssignableFrom(clazz) 
			&& validationManager.supports(clazz);
	}

	@Override
	public void validate(Object target, Errors errors) {
		validationManager.validate(target, errors);
		if (RequestContextHolder.getRequestContext() != null) {
			String stateId = 
				RequestContextHolder.getRequestContext().getCurrentState().getId();
			String flowId = 
				RequestContextHolder.getRequestContext().getCurrentState().getOwner().getId();
			doManualValidation(flowId, stateId, target, errors);
		}
	}
	
	/**
	 * Overload doManualValidation if it is wished to perform 
	 * validation in addition to what is specified in the rules.
	 * @param flowId Provides the Web Flow Flow ID.
	 * @param stateId Provides the Web Flow State ID.
	 * @param target Provides the Object being validated.
	 * @param errors Provides an Errors interface for recording validation messages.
	 */
	public void doManualValidation(String flowId, String stateId, Object target, Errors errors) {
		return;
	}

}
